package program.services;

import com.sun.prism.shader.Solid_TextureYV12_AlphaTest_Loader;
import program.domain.Memory;
import program.domain.PCB;
import program.utils.PCBQueue;
import program.utils.PCBQueue;

import java.nio.file.attribute.UserDefinedFileAttributeView;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;


/**
 * The type First fit.
 *
 * @program: OSAlgorithms
 * @description: 最先适应分配
 * @author: Echo -dev
 * @create: 2021 -11-05 12:04
 */
public class FirstFit {
    /**
     * 空闲区
     */
    public ArrayList<Memory> free = new ArrayList<>();

    /**
     * 待分配内存的进程
     */
    public PCBQueue ready = new PCBQueue();

    /**
     * 运行结束的进程
     */
    public PCBQueue finished = new PCBQueue();

    /**
     * 已分配区
     */
    public ArrayList<Memory> used = new ArrayList<>();

    /**
     * Gets free.
     *
     * @return the free
     */
    public ArrayList<Memory> getFree() {
        return free;
    }

    /**
     * Sets free.
     *
     * @param free the free
     */
    public void setFree(ArrayList<Memory> free) {
        this.free = free;
    }

    /**
     * Gets ready.
     *
     * @return the ready
     */
    public PCBQueue getReady() {
        return ready;
    }

    /**
     * Sets ready.
     *
     * @param ready the ready
     */
    public void setReady(PCBQueue ready) {
        this.ready = ready;
    }

    /**
     * Gets finished.
     *
     * @return the finished
     */
    public PCBQueue getFinished() {
        return finished;
    }

    /**
     * Sets finished.
     *
     * @param finished the finished
     */
    public void setFinished(PCBQueue finished) {
        this.finished = finished;
    }

    /**
     * Gets used.
     *
     * @return the used
     */
    public ArrayList<Memory> getUsed() {
        return used;
    }

    /**
     * Sets used.
     *
     * @param used the used
     */
    public void setUsed(ArrayList<Memory> used) {
        this.used = used;
    }

    /**
     * 初始化内存区域排列
     */
    public void init() {
        //对空闲区根据起始地址排序
        Collections.sort(free, new Comparator<Memory>() {
            @Override
            public int compare(Memory o1, Memory o2) {
                if (o1.getStart() > o2.getStart()) {
                    return 1;
                }
                else {
                    return -1;
                }
            }
        });
    }

    /**
     * 分配内存
     */
    public void allocate() {
        if (!ready.isEmpty()) {
            PCB pcb = ready.remove();
            //完成分配的标志
            boolean tag = false;
            int needSize = pcb.getMemorySize();
            for (int i = 0; i < free.size(); i++) {
                //遍历空闲区表
                Memory freeZone = free.get(i);
                if (freeZone.getLength() >= needSize) {
                    //空间满足分配要求
                    Memory allocated = new Memory(freeZone.getStart(), needSize, pcb);
                    freeZone.setPcb(pcb);
                    used.add(allocated);
                    //改变空闲区表
                    int newLength = freeZone.getLength() - needSize;
                    if (newLength != 0) {
                        freeZone.setStart(freeZone.getStart() + needSize);
                        freeZone.setLength(newLength);
                    }
                    else {
                        free.remove(freeZone);
                    }
                    tag = true;
                    break;
                }
            }
            if (!tag) {
                //剩余内存无法满足分配
                System.out.println("Out of memory");
                //break;
            }
            else {
                //打印信息
                System.out.println("一次分配结束，空闲区：");
                for (int i = 0; i < free.size(); i++) {
                    System.out.println("起始地址:" + free.get(i).getStart() + "    大小:" + free.get(i).getLength());
                }
            }

        }

    }

    /**
     * 检查是否需要回收内存
     */
    public void checkFinish() {
        for (int i = 0; i < used.size(); i++) {
            if (used.get(i).getPcb() != null) {
                System.out.println(used.get(i).getPcb().getCmdName() + " :  " + used.get(i).getPcb().getNeedTime());
            }
            if (used.get(i).getPcb() != null && used.get(i).getPcb().getNeedTime() == 0) {
                //进程运行结束,回收
                finished.add(used.get(i).getPcb());
                Memory freeMemory = new Memory();
                freeMemory.setStart(used.get(i).getStart());
                freeMemory.setLength(used.get(i).getLength());
                //if (!ifCanMerge(freeMemory)){
                free.add(freeMemory);
                merge();
                //}
                used.get(i).setPcb(null);
            }
        }
    }

    /**
     * If can merge boolean.
     *
     * @param memory the memory
     * @return the boolean
     */
    public boolean ifCanMerge(Memory memory) {
        int num1 = memory.getStart() + memory.getLength();
        int num2 = memory.getStart() - memory.getLength();
        for (int i = 0; i < free.size(); i++) {
            if (num1 == free.get(i).getStart()) {
                free.get(i).setStart(memory.getStart());
                free.get(i).setLength(memory.getLength() + free.get(i).getLength());
                return true;
            }
        }
        for (int i = 0; i < free.size(); i++) {
            if (num2 == free.get(i).getStart()) {
                free.get(i).setLength(memory.getLength() + free.get(i).getLength());
                return true;
            }
        }
        return false;
    }

    /**
     * 回收后合并相邻空闲区
     */
    public void merge() {
        init();
        //使用迭代器删除
        Iterator<Memory> iterator = free.iterator();
        Memory preMemory = free.get(0);
        Memory tmp = null;
        int preStart;
        int preLength;
        while (iterator.hasNext()) {
            Memory curMemory = iterator.next();
            if (!(preMemory.getStart()+preMemory.getLength()==curMemory.getStart())&&tmp!=null){
                preMemory = tmp;
            }
            preStart = preMemory.getStart();
            preLength = preMemory.getLength();
            if ((preStart + preLength) == curMemory.getStart()) {
                System.out.println("MERGE"+"--pre:"+preStart+"--cur:"+curMemory.getStart());
                System.out.println("MERGE"+"--pre:"+preLength+"--cur:"+curMemory.getLength());
                curMemory.setStart(preStart);
                preMemory.setLength(preLength + curMemory.getLength());
                iterator.remove();
            }
            tmp = curMemory;
        }
    }

}
